; Example Source Skeleton written by tyro@deco.franken.de
;
; "I worked damn long to add all those comments, so PLEASE leave this
; note in when spreading the file or putting a copy on your own page..."
.include "sfr.i"
; To enable the assembler to recognize all Special Function Registers and
; PSW bits, you need the file sfr.i, which should be placed in the same
; directory where the assembler is.
* ------------------------------------------------------
* Variable-Definitions
; Here you can define variables to store data. For example, if you want
; to store a counter value somewhere, you can define a variable named
; 'counter' with the following code:
;
; counter = $30
;
; $30 being the memory address where the variable is stored
;
; Now you can write to that address by using the assigned name, for example
;
; MOV #$00,COUNTER
;
; will store value $00 at memory address $30. Or
;
; LD COUNTER
;
; will load the value stored in memory address $30 into the ACC register.
* ------------------------------------------------------
* Reset- and Interrupt-Vectors
; The following are interrupt vectors used by the VMU hardware. DO NOT
; CHANGE ANY OF THEM.
;
; .org
means that the code following the .org directive starts
; at the given address. For example
;
; .org 200
;
; XOR ACC
;
; means that 'XOR ACC' will be placed at memory offset $200 in the program,
.org 0
jmpf start ; far jump to the start of the main program
.org $3
jmp nop_irq
.org $b
jmp nop_irq
.org $13
jmp nop_irq
.org $1b
jmp t1int
.org $23
jmp nop_irq
.org $2b
jmp nop_irq
.org $33
jmp nop_irq
.org $3b
jmp nop_irq
.org $43
jmp nop_irq
.org $4b
clr1 p3int,0
clr1 p3int,1
nop_irq:
reti
.org $130
t1int:
push ie
clr1 ie,7
not1 ext,0
jmpf t1int
pop ie
reti
.org $1f0
goodbye:
not1 ext,0
jmpf goodbye ; leave game mode
* ------------------------------------------------------
* Game Header
; This is the game header, containing info about the game.
; 16 byte Game Description, 32 byte Copyright Info.
;
; The directive .byte means that the following is data that should
; be stored into memory. Examples:
;
; .byte $0c a byte with value $0c (12 decimal)
; .byte 'a' a byte with value $61 (ASCII 'a')
; .byte %00001001 a byte with value $09 (binary notation)
.org $200
.byte "Test Skeleton " ; 16 byte
.byte "written by tyro@deco.franken.de " ; 32 byte
* ------------------------------------------------------
* Game Icon Definition
; Here is defined how many game icons (which are shown in the Dreamcast
; File Manager) are stored in the header, and which what animation speed
; they are displayed.
;
; .word is the same as .byte, only that it's 16 bit long instead of 8 bit.
.org $240
.word 1 ; number of icons (max = 3)
.word 10 ; animation speed
* ------------------------------------------------------
* Game Icon Palette Table
; Here you can define up to 16 different colors for the game icons.
;
; Each color is 4 * 4 bit: Alpha-Contrast / Red / Green / Blue
;
; Example colors:
;
; 1111 0000 0000 0000 $ f000 black
;
; 1111 1111 1100 1100 $ ffcc lt. red
; 1111 1010 0000 0000 $ fa00 dark red
; 1111 1111 0000 0000 $ ff00 red
;
; 1111 1100 1111 1100 $ fcfc lt. green
; 1111 0000 1010 0000 $ f0a0 dark green
; 1111 0000 1111 0000 $ f0f0 green
;
; 1111 1100 1100 1111 $ fccf lt. blue
; 1111 0000 0000 1010 $ f00a dark blue
; 1111 0000 0000 1111 $ f00f blue
;
; 1111 1111 1111 1000 $ fff8 lt. yellow
; 1111 1101 1101 0000 $ fdd0 dark yellow
; 1111 1111 1111 0000 $ fff0 yellow
;
; 1111 1000 1111 1111 $ f8ff lt. cyan
; 1111 0000 1010 1010 $ f0aa dark cyan
; 1111 0010 1111 1111 $ f2ff cyan
;
; 1111 1111 1000 1111 $ ff8f lt. purple
; 1111 1100 0000 1100 $ fc0c dark purple
; 1111 1111 0001 1111 $ ff1f purple
;
; 1111 1111 1111 1111 $ ffff white
.org $260
.word $f000, $ff00, $f0f0, $f00f, $fff0, $faff, $ff1f, $ffff
; black, red, green, blue, yellow, cyan, purple, white
.word $ffff, $ffff, $ffff, $ffff, $ffff, $ffff, $ffff, $ffff
; white, white, white, white, white, white, white, white
* ------------------------------------------------------
* Game Icon Data
; This is where the game icons are placed. Each icon is 512 byte long,
; 32 * 32 pixel, each pixel being represented by 4 bit (one nybble) that
; serve as a pointer into the color palette table.
;
; This example image just shows a yellow background (all nybbles pointing
; to color 4 in the color palette table).
.org $280
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
.byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44
* ------------------------------------------------------
* Main Program
; the following code is mandatory to make the game work - just leave
; it 'as is'.
start:
mov #$a1,ocr ; write $a1 into Oscillation Control Register (OCR)
mov #$09,mcr ; write $09 into Mode Control Register (MCR)
mov #$80,vccr ; write $80 into LCD Contrast Control Register (VCCR)
clr1 p3int,0 ; clear bit 0 in Port 3 Interrupt Control Register
; (P3INT)
clr1 p1,7 ; clear bit 7 in Port 1 Latch (P1)
mov #$ff,p3 ; write $ff in Port 3 Latch (P3)
; Your own program code starts here. ;)
;
; The following stuff only serves as an example to demonstrate how to
; handle the LCD screen and keypresses. Modify it as you please.
call clrscr ; this clears the LCD screen
; TRL and TRH are special function registers used to hold the high byte (TRH)
; and low byte (TRL) of an address. The 16-bit address in those registers,
; together with the value in the ACC register, form an indirect memory address.
; When the LDC instruction is issued, the byte located at (TRH/TRL + ACC) is
; loaded into ACC.
mov #screen_data,trh
call setscr ; this copies an image to the LCD screen
; The GETKEYS function (taken from Marcus Comstedt's "Tetris" program)
; doesn't really need any explanation, simply use it 'as is', like everyone
; else does. ;)
.keypress:
call getkeys
bn acc,4,.keypress ; branch if button A is pressed
bn acc,5,.keypress ; branch if button B is pressed
;
br .keypress ; branch even if neither A nor B are pressed
; The following are subroutines.
* ------------------------------------------------------
* Clear Screen
clrscr:
clr1 ocr,5
;
push acc
push xbnk
push 2
;
mov #0,xbnk
.cbank:
mov #$80,2
.cloop:
mov #0,@R2
;
inc 2
;
ld 2
and #$f
bne #$c,.cskip
;
ld 2
add #4
st 2
.cskip:
ld 2
bnz .cloop
;
bp xbnk,0,.cexit
;
mov #1,xbnk
br .cbank
.cexit:
pop 2
pop xbnk
pop acc
set1 ocr,5
ret
; Explanation:
;
; clr1 ocr,5
;
; The clr1 instruction clears the fifth bit in the Oscillation Control
; Register. The previous hexadecimal value was A1, which is the binary
; number 10100001. The CPU recognizes the right most significant bit as
; the zeroeth bit; the new number in the OCR is 10000001, or 81 in
; hexadecimal. This new value changes the clock speed to 5 Mhz, the
; appropriate speed for accessing the LCD Frame Buffer of the VMU.
;
; push acc
; push xbnk
; push 2
;
; The values in the accumulator, the bank address register, and memory
; address 002 are stored on the stack.
;
; mov #0,xbnk
;
; Placing the value 0 into the Bank Address Register sets it to zero. The
; LCD Frame Buffer is only large enough for half of the LCD screen; bank zero
; lets us work with the top half of the LCD screen.
;
; .cbank:
; mov #$80,2
;
; The LCD frame buffer starts at address 0180. So the starting value $80 is
; placed into memory address 2.
;
; .cloop:
; mov #0,@R2
;
; @R2 is used for indirect addressing and serves as a pointer into the Special
; Function Register Area of the VMU (of which locations $80 - $fb represent
; the LCD Frame Buffer (XRAM) - @R0 and @R1 always point to the General
; Purpose RAM (from 0000 - 00ff), while @R2 and @R3 always point to the
; Special Function Register area (from 0100 - 01ff).
;
; During the first pass, memory address two holds the value $80. Therefore,
; 00000000 is placed into memory address 0180. We've now cleared the first 8
; pixels on the LCD screen.
;
; inc 2
;
; We increment the value of memory address 002. During the first pass, this
; would change it to $81.
;
; ld 2
;
; Now we put the value of memory address 002 into the accumulator. Again, in
; the first pass this is $81.
;
; and #$f
;
; The CPU performs a bitwise AND with the value in the accumulator and $0F.
; During the first pass, we're comparing 10000001 and 00001111; the new value
; is 00000001.
;
; bne #$c,.cskip
;
; This instruction is a conditional statement (an "if"). If the value of the
; accumulator is equal to $0C (12 decimal), goto (branch to) subfunction
; ".cskip". We do this to see if the LCD frame buffer address is divisable
; by 12. Each six bytes (6 * 8 bits) in the LCD Frame Buffer represent one
; line on the LDC screen (48 pixel), and after every two lines there is a
; space of 4 unused bytes.
;
; ld 2
; add #4
; st 2
;
; If we have written two full lines to the the LCD Frame Buffer (of which the
; current write address is represented in memory address 002), then we add 4,
; to skip the unused bytes and set the pointer to the beginning of the next
; line. During the first pass it isn't, so this part is skipped.
;
; .cskip:
; ld 2
; bnz .cloop
;
; This is a check to see if we're finished with the LCD Frame Buffer in
; bank 0 (half of the screen). If we've reached the end and roll over to 00,
; go back to ".cloop".
;
; bp xbnk,0,.cexit
;
; If the Bank Address Register does not equal zero (meaning that we have
; already written to the LCD Frame Buffer in both banks), goto ".cexit".
;
; mov #1,xbnk
; br .cbank
;
; Move 1 into the Bank Address Register (switch to bank 1). This lets us
; clear the bottom half of the screen. We then continue with writing to the
; lower half of the screen just like we previously wrote to the upper half
; (because all that's different is that the lower half is in another bank).
;
; .cexit:
; pop 2
; pop xbnk
; pop acc
; set1 ocr,5
; ret
;
; This restores the values of memory address 2, the Bank Address Register,
; and the Accumulator. The clock speed is reset to A1 and the function
; returns to the address following the code that called it.
* ------------------------------------------------------
* Set Screen
setscr:
clr1 ocr,5
push acc
push xbnk
;
push c
push 2
mov #$80,2
;
xor acc
st xbnk
st c
.sloop:
ldc
st @R2
;
inc 2
;
ld 2
and #$f
bne #$c,.sskip
;
ld 2
add #4
st 2
;
bnz .sskip
;
inc xbnk
mov #$80,2
.sskip:
inc c
ld c
bne #$c0,.sloop
;
pop 2
pop c
pop xbnk
pop acc
set1 ocr,5
ret
; Explanation:
;
; clr1 ocr,5
; push acc
; push xbnk
;
; Setscr is similar to clrscr. The C register is used to as a counter for
; the table data (the picture we're going to copy to the LCD screen).
;
; push c
; push 2
; mov #$80,2
;
; xor acc
; st xbnk
; st c
;
; By XORing the accumulator with itself, we are setting it to zero.
;
; .sloop:
; ldc
;
; LDC creates a 16-bit address by combining the 16-bit address in TRH/TRL
; with the value in the accumulator. The byte at this address (the beginning
; of our picture data) is then loaded into the accumulator.
;
; In the first pass, ACC contains the value zero, so the value at address
; (TRH/TRL + 0) is loaded.
;
; st @R2
;
; We then write this data into the LCD Frame Buffer.
;
; inc 2
;
; ld 2
; and #$f
; bne #$c,.sskip
;
; ld 2
; add #4
; st 2
;
; bnz .sskip
;
; inc xbnk
; mov #$80,2
;
; All of the above is similar to clrscr.
;
; .sskip:
; inc c
; ld c
;
; Here we increase C by one, and load the new value into the accumulator.
;
; bne #$c0,.sloop
;
; If the accumulator has not yet reached hexadecimal value C0 (192 decimal,
; 6 bytes per screenline * 32 lines), we branch back to ".sloop". Since the
; accumulator now contains a value that has been increased by one, during this
; pass we will now load the byte from our screen-data that follows the one we
; loaded during our previous pass.
;
; pop 2
; pop c
; pop xbnk
; pop acc
; set1 ocr,5
; ret
;
; Again the previously saved values are restored and the function returns to
; the address following the code that called it.
screen_data:
;
; 32 lines with 6 bytes (48 pixel) per line ("Hello World" image)
;
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000100,%01001111,%10010000,%00100000,%00111000,%00000000
.byte %00000100,%01001000,%00010000,%00100000,%01000100,%00000000
.byte %00000111,%11001111,%00010000,%00100000,%01000100,%00000000
.byte %00000100,%01001000,%00010000,%00100000,%01000100,%00000000
.byte %00000100,%01001111,%10011111,%00111110,%00111000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000100,%01000111,%00011110,%00100000,%01111000,%11100000
.byte %00000100,%01001000,%10010001,%00100000,%01000100,%11100000
.byte %00000101,%01001000,%10011110,%00100000,%01000100,%01000000
.byte %00000101,%01001000,%10010100,%00100000,%01000100,%00000000
.byte %00000010,%10000111,%00010010,%00111110,%01111000,%01000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
.byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000
* ----------------------------------------------------------------------
* getkeys - loads Port 3 data into ACC
* also handles Mode-button (QUIT), Dreamcast Connect and Sleep
getkeys:
bp p7,0,quit ; Quit, if Dreamcast Connection
ld p3 ; read key status
bn acc,6,quit ; if Mode Key pressed, Quit
bn acc,7,sleep ; if Sleep Key pressed, then Pause
ret ; otherwise return with pressed key in ACC
quit:
jmp goodbye ; Long Jump, in case we are too far away
; for a 'normal' branch
sleep:
set1 pcon,0 ; activate HALT mode (saves power)
bn p3,7,sleep ; wait until Sleep Key is released
mov #0,vccr ; turn off LCD
;
sleepmore:
set1 pcon,0 ; activate HALT mode (saves power)
bp p7,0,quit ; Docked?
bp p3,7,sleepmore ; no Sleep Key pressed yet
mov #$80,vccr ; turn on LCD again
;
waitsleepup:
set1 pcon,0 ; activate HALT modus (saves power)
bn p3,7,waitsleepup
br getkeys ; continue to wait for keypress
* ----------------------------------------------------------------------
* End
.cnop 0,$200 ; pad to an even number of blocks